package org.kernely.ui;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.kernely.core.service.KernelyBundleService;
import org.kernely.security.service.KernelySecurityService;
import org.kernely.ui.login.KernelyLoginEvent;
import org.kernely.ui.login.KernelyLoginPresenter;
import org.kernely.ui.login.KernelyLoginView;
import org.kernely.ui.mvp.IKernelyView;
import org.kernely.ui.mvp.KernelyEvent;
import org.kernely.ui.mvp.KernelyEventBus;
import org.kernely.ui.mvp.KernelyEventBusGeneral;
import org.kernely.ui.mvp.KernelyEventReceiver;
import org.kernely.ui.mvp.KernelyEventSender;
import org.kernely.ui.mvp.KernelyPresenter;
import org.kernely.ui.services.KernelyUIBundle;
import org.kernely.ui.services.KernelyUIBundleService;
import org.kernely.ui.services.KernelyUIBundleServiceListener;

import com.vaadin.Application;
import com.vaadin.service.ApplicationContext;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

/**
 * MainApplication class
 */
public class MainApplication extends Application implements ApplicationContext.TransactionListener, KernelyUIBundleServiceListener, KernelyEventReceiver, KernelyEventSender {

	/**
	 * Kernely declarative services
	 */
	private static KernelyBundleService coreService;
	private static KernelySecurityService securityService;
	private static KernelyUIBundleService uiBundleService;

	// Vaadin objects
	private Window window;
	private VerticalLayout mainLayout;
	private IKernelyView loginView;
	private IKernelyView mainView;
	private KernelyPresenter loginPresenter;
	private KernelyPresenter mainPresenter;

	private static KernelyEventBus eventBusMA;

	/**
	 * The serialization id.
	 */
	private static final long serialVersionUID = 1342675030168502215L;

	/**
	 * MainApplication's Servlet
	 */
	public static class VaadinServlet extends AbstractApplicationServlet {

		/**
		 * The Serial Version ID
		 */
		private static final long serialVersionUID = 102610594159015L;

		/**
		 * Retrieve the application's class associated to this servlet
		 */
		@SuppressWarnings("unchecked")
		@Override
		final protected Class getApplicationClass() {
			return MainApplication.class;
		}

		/**
		 * Retrieve a new instance of the application associated to this servlet
		 */
		@Override
		final protected Application getNewApplication(HttpServletRequest request) throws ServletException {
			return new MainApplication();
		}

	}

	/**
	 * Get the coreService
	 * @return the core service
	 */
	public static KernelyBundleService getCoreService() {
		return coreService;
	}

	/**
	 * Set the coreService
	 * 
	 * @param coreService
	 *            : the KernelyBundleService service
	 */
	public static void setCoreService(KernelyBundleService coreService) {
		if (MainApplication.coreService == null) {
			MainApplication.coreService = coreService;
		}
	}

	/**
	 * This function initializes the main window of Kernely.
	 */
	@Override
	public final void init() {
		uiBundleService.addListener(this);

		// We register to the event bus to be fired by events
		if(SecurityUtils.getSecurityManager() != null){

			Session session = (Session) SecurityUtils.getSubject().getSession();
			KernelyEventBus eventBus = (KernelyEventBus) session.getAttribute("eventBus");
			if (eventBus == null) {
				eventBus = new KernelyEventBus();
				session.setAttribute("eventBus", eventBus);
			}
			eventBus.addListener(this);

			eventBusMA = eventBus;
			mainLayout = new VerticalLayout();
			mainLayout.setHeight(100, Sizeable.UNITS_PERCENTAGE);

			// We instantiate Views and Presenters for later.
			loginView = new KernelyLoginView();
			loginPresenter = new KernelyLoginPresenter();

			mainView = new MainApplicationView();
			mainPresenter = new MainApplicationPresenter();

			window = new Window("Kernely: Portal");
			window.setHeight(100, Sizeable.UNITS_PERCENTAGE);
			window.setSizeFull();

			window.setContent(mainLayout);

			// Set the immediate display mode
			window.setImmediate(true);
			setTheme("kernelytheme");
			createMainWindow();
		}
		else{
			System.out.println("Security manager is null");
		}
	}

	/**
	 * Create the main window of the application
	 */
	public final void createMainWindow() {
		// We retrieve the current user and check if he's authenticated or not.
		Subject user = SecurityUtils.getSubject();
		if (user.isAuthenticated()) {
			showApplicationPage();
		} else {
			showLoginPage();
		}
		// Define the window as main window
		setMainWindow(window);
	}

	/**
	 * Create the login page's view
	 */
	public final void showLoginPage() {
		// Association View - Presenter
		loginPresenter.setView(loginView);
		VerticalLayout view = (VerticalLayout) loginPresenter.getView();
		mainLayout.addComponent(view);
	}

	/**
	 * Create the application page's view
	 */
	public final void showApplicationPage() {
		// Association View - Presenter
		mainPresenter.setView(mainView);
		mainLayout.replaceComponent((VerticalLayout) loginView, (VerticalLayout) mainView);
	}

	/**
	 * Called when a KernelyViewEvent is fired.
	 */
	public final void handleKernelyEvent(KernelyEvent event) {
		if (event instanceof KernelyLoginEvent) {
			createMainWindow();
		}
	}

	/**
	 * Gets the Kernely event bus.
	 * 
	 * @return the eventBus
	 */
	public static KernelyEventBus getEventBus() {
		Session session = (Session) SecurityUtils.getSubject().getSession();
		return (KernelyEventBus) session.getAttribute("eventBus");
	}

	/**
	 * Sets the KernelyUIBundleService Declarative Service.
	 * 
	 * @param uiBundleService
	 *            : the KernelyUIBundleService service
	 */
	public final void setUIBundleService(KernelyUIBundleService uiBundleService) {
		MainApplication.uiBundleService = uiBundleService;
	}

	/**
	 * Unsets the KernelyUIBundleService Declarative Service.
	 * 
	 * @param uiBundleService
	 *            : the KernelyUIBundleService service
	 */
	public final void unsetUIBundleService() {
	}


	/**
	 * Sets the security Declarative Service
	 * @param service The security service.
	 */
	public final void setSecurityService(KernelySecurityService service) {
		securityService = service;
	}

	/**
	 * Unsets the security Declarative Service
	 * @param service The security service.
	 */
	public final void unsetSecurityService(KernelySecurityService service) {}

	/**
	 * Gets the security service.
	 * @return the security service.
	 */
	public final static KernelySecurityService getSecurityService() {
		return securityService;
	}


	public final static KernelyUIBundleService getKernelyUIBundleService() {
		return uiBundleService;
	}

	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * started
	 * 
	 * @param kbs
	 *            : the kernely bundle service (core)
	 */
	public final void setModuleService(KernelyBundleService kbs) {
	}

	/**
	 * Called by the Service Component Runtime when the declarative service is
	 * stopped
	 * 
	 * @param kbs
	 *            : the kernely bundle service (core)
	 */
	public final void unsetModuleService(KernelyBundleService kbs) {
	}

	/**
	 * Called when a new UI bundle is detected
	 */
	@Override
	public final void moduleRegistered(KernelyUIBundleService source, KernelyUIBundle module) {

		// We warn the general event bus that something changed in ui modules.
		KernelyEventBusGeneral.getInstance().broadcastEvent(new KernelyUIBundleDetectionEvent(this));
	}

	/**
	 * Called when an UI bundle is removed
	 */
	@Override
	public final void moduleUnregistered(KernelyUIBundleService source, KernelyUIBundle module) {

		// We warn the general event bus that something changed in ui modules.
		KernelyEventBusGeneral.getInstance().broadcastEvent(new KernelyUIBundleDetectionEvent(this));
	}

	@Override
	public final void transactionStart(Application application, Object transactionData) {
		System.out.println("Transaction begin");

	}

	@Override
	public final void transactionEnd(Application application, Object transactionData) {
		System.out.println("Transaction end");

	}


}